import time

from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
from PIL import Image

from qturboframe.lib.webdriver.exceptions import SeleniumError
from qturboframe.lib.webdriver.remote.webdriver import WebDriver
from qturboframe.lib.webdriver.remote.webelement import WebElement
from qturboframe.lib.report.log import logger

from qturboframe.public_ui.base_element import BaseElement as PuBaseElement


class BaseElement(PuBaseElement):
    '''
    Jres组件基础控件类

    :Attributes:
     - driver: 控件所在的当前webdriver对象
     - element: 控件元素webelement对象
     - timeout: 步骤操作的超时时间

    :Example:
     - 例1: BaseElement(webelement)
     - 例2: BaseElement('id')
     - 例3: BaseElement({By.NAME:'name'})
     - 例4: BaseElement({By.CLASS_NAME:'class_name','text':'文本信息'})
     - 例5: BaseElement({By.CLASS_NAME:'class_name','text':'文本信息'}, father=driver)
     - 例6: BaseElement({By.CLASS_NAME:'class_name','text':'文本信息'}, father=webelement)
    '''

    def __init__(self, element_para, father=None, timeout=5):
        # element_para入参为字符串类型时，默认为查询方式为id
        if isinstance(element_para, str):
            element_para = {By.ID:element_para}
        if isinstance(element_para, WebElement):
            self._elements = [element_para]
            self._element = element_para
            self._driver = element_para._parent
            self.timeout = timeout
        elif not isinstance(element_para, dict):
            raise AttributeError('element_para参数类型不正确！')
        else:
            if father is None:
                raise AttributeError('未定位元素，father参数不能为None！')
            if isinstance(father, WebDriver):
                self._driver = father
                self.timeout = timeout
            elif isinstance(father, WebElement):
                self._driver = father._parent
                self.timeout = timeout
            else:
                raise AttributeError('father参数类型不正确！')
            key_list = [By.ID, By.NAME, By.CLASS_NAME, By.TAG_NAME, By.LINK_TEXT,
                        By.PARTIAL_LINK_TEXT,By.CSS_SELECTOR, By.XPATH]
            element_by = ''
            element_value = ''
            assist = ''
            for key, value in element_para.items():
                if key in key_list:
                    element_by = key
                    element_value = value
                else:
                    assist += '{}:{},'.format(key, value)
            assist = assist.strip(',')

            self._element = None
            #解决异常问题：element is not attached to the page document
            for i in range(int(timeout)):
                self._elements = father.find_elements_by_attribute(
                    element_by, element_value, assist, timeout=1)

                try:
                    for element in self._elements:
                        if element.is_displayed():
                            self._element = element
                            break
                        else:
                            element.location_once_scrolled_into_view
                            if element.is_displayed():
                                self._element = element
                                break
                    if self._element:
                        break
                except Exception as msg:
                    logger.debug(msg)
                    time.sleep(1)
            else:
                if len(self._elements) == 0:
                    raise SeleniumError("没有找到locator为{}的元素".format(element_para))
                #当没有找到可见的元素时，则返回第一个元素
                self._element = self._elements[0]
                logger.info("按照此locator:{}没有找到可见元素，返回了DOM中第一个元素符合此locator的元素".format(element_para))

    def get_text(self):
        '''
        获取元素的文本

        :Returns:
            返回元素的文本

        :Example:
         - 例: text = base_element.get_text()
        '''
        text = self._element.text if self._element.text else self._element.get_attribute("innerText")
        return text

    def click(self):
        '''
        点击元素

        :Example:
         - 例: base_element.click()
        '''
        self._driver.execute_script("arguments[0].click();", self._element)

    @property
    def element(self):
        '''
        获取元素webelement对象

        :Returns:
            返回元素webelement对象

        :Example:
         - 例: element = base_element.element
        '''
        return self._element

    def verify(self):
        """
        jres2.0的表单控件验证是否成功

        :Args:
         - inputele: 表单控件

        :Returns:
            验证成功返回True，验证失败返回False，无验证结果则返回None
        """
        classValue = self._element.get_attribute('class')
        if 'm-verify-success' in classValue:
            return True
        elif 'm-verify-error' in classValue:
            return False
        else:
            return None

    def get_error_info(self):
        """
        获取jres2.0的表单控件验证错误信息

        :Args:
         - inputele: 表单控件

        :Returns:
            返回验证错误的文本信息
        """
        xpath = "./parent::div/following-sibling::div[1]/div[@class='verify-tip-inner']"
        div_error = self._element.find_element_by_attribute('xpath', xpath, timeout=self.timeout)
        return div_error.text

    def is_default(self, value):
        """
        检查jres2.0的表单控件默认值

        :Args:
         - value: 待检查的值

        :Returns:
            检查匹配返回True，否则返回False
        """
        if self._element.text:
            text = self._element.text
        elif self._element.get_attribute('defaultvalue'):
            text = self._element.get_attribute('defaultvalue')
        elif self._element.get_attribute('value'):
            text = self._element.get_attribute('value')
        elif self._element.get_attribute('placeholder'):
            text = self._element.get_attribute('placeholder')
        elif self._element.get_attribute('title'):
            text = self._element.get_attribute('title')
        else:
            text = ""
        if text == value:
            logger.info("校验成功, 期望值为:{}, 实际值为:{}".format(value, text))
            return True
        else:
            raise SeleniumError("校验失败, 期望值为:{}, 实际值为:{}".format(value, text))
            return False

    def move_to_element(self):
        """
        hover到指定控件上
        """
        for i in range(5):
            try:
                action = ActionChains(self._driver)
                action.move_to_element(self._element).click().perform()
                break
            except Exception as msg:
                logger.debug(msg)
                time.sleep(1)

    def set_style_display_block(self):
        """
        用js设置控件属性display:block
        """
        js = "arguments[0].style.display='block';"
        self._driver.execute_script(js, self._element)

    def set_style_display_none(self):
        """
        用js设置控件属性display:none
        """
        js = "arguments[0].style.display='none';"
        self._driver.execute_script(js, self._element)

    def take_element_screenshot(self, save_path):
        '''
        获取某一个元素的截图

        :Args:
         - save_path: 保存截图的路径

        :Example:
         - 例1: base_element.take_element_screenshot('C:\Pictures\Saved Pictures\image.png')
        '''
        # 滚动到浏览器顶部
        js_top = "var q=document.documentElement.scrollTop=0"
        self._driver.execute_script(js_top)
        time.sleep(2)

        temp_path = "temp.png"
        self._driver.save_screenshot(temp_path)

        location = self._element.location
        size = self._element.size
        x = location['x']
        y = location['y']
        w = x + size['width']
        h = y + size['height']

        full_image = Image.open(temp_path)
        crop_image = full_image.crop((x, y, w, h))
        crop_image.save(save_path)

